#ifndef VDMA_DISPLAY_H
#define VDMA_DISPLAY_H

#include "xil_types.h"     // 包含 u32, UINTPTR 等类型定义
#include "xparameters.h"   // 包含 VDMA 设备 ID、内存地址等宏定义 (SDT模式下会提供 XPAR_AXIVDMA_0_BASEADDR)
#include "xaxivdma.h"      // VDMA 驱动的必要类型，如 XAxiVdma_Config
#include <stdbool.h>

#include "FreeRTOS.h"
#include "semphr.h"


// --- VDMA 硬件输出分辨率锁定为 640x480 ---
#define VDMA_OUTPUT_WIDTH           640
#define VDMA_OUTPUT_HEIGHT          480

// --- 新增: HLS IP 实际渲染的分辨率 (320x240) ---
#define HLS_RENDER_WIDTH            320
#define HLS_RENDER_HEIGHT           240


// VDMA 期望的像素格式是 BGR (3 字节/像素)
#define VDMA_BYTES_PER_PIXEL        3 
// HLS IP 写入 DDR 的实际像素格式是 BGRX (4 字节/像素)
#define HLS_BYTES_PER_PIXEL         4

// VDMA 单帧缓冲区大小 (水平分辨率 * 垂直分辨率 * 每像素字节数)
#define VDMA_SINGLE_FRAME_SIZE      ((u32)VDMA_OUTPUT_WIDTH * VDMA_OUTPUT_HEIGHT * VDMA_BYTES_PER_PIXEL)
// HLS IP 写入的单帧缓冲区大小 (使用 HLS_RENDER_WIDTH 和 HLS_RENDER_HEIGHT)
#define HLS_SINGLE_FRAME_SIZE       ((u32)HLS_RENDER_WIDTH * HLS_RENDER_HEIGHT * HLS_BYTES_PER_PIXEL)

// --- 三缓冲定义 ---
#define VDMA_NUM_FRAME_BUFFERS      3 // 定义为三缓冲

// --- 帧缓冲区内存布局 ---
// 所有帧缓冲区从 DDR 的一个连续区域开始分配
#define FRAME_BUFFER_DDR_START_ADDR (UINTPTR)(XPAR_PS7_DDR_0_BASEADDRESS + 0x1000000)

// HLS IP 写入的帧缓冲区将首先分配 (使用 HLS_SINGLE_FRAME_SIZE)
#define HLS_FRAME_BUFFER_BASE_ADDR  FRAME_BUFFER_DDR_START_ADDR
// VDMA 读取的帧缓冲区紧接着 HLS 帧缓冲区之后 (使用 VDMA_SINGLE_FRAME_SIZE)
#define VDMA_FRAME_BUFFER_BASE_ADDR (HLS_FRAME_BUFFER_BASE_ADDR + (HLS_SINGLE_FRAME_SIZE * VDMA_NUM_FRAME_BUFFERS))


// VdmaDisplay 结构体封装 VDMA 驱动和硬件操作
typedef struct {
    XAxiVdma vdmaInstance_;            // VDMA 驱动实例
    XAxiVdma_Config *vdmaConfig_;     // VDMA 硬件配置信息
    XAxiVdma_DmaSetup vdmaDmaSetup_;  // VDMA DMA 配置设置
    
    // HLS IP 写入的帧缓冲区的基地址数组 (4 字节/像素)
    UINTPTR hlsFrameBufferBaseAddr_[VDMA_NUM_FRAME_BUFFERS]; 
    // VDMA 读取的帧缓冲区的基地址数组 (3 字节/像素)
    UINTPTR vdmaFrameBufferBaseAddr_[VDMA_NUM_FRAME_BUFFERS];

    // 跟踪下一个 HLS/GPU 要写入的缓冲区索引
    volatile int nextHlsWriteBufferIdx_; 
    // 跟踪当前 VDMA 正在显示的缓冲区索引 (或上一次提交的缓冲区)
    volatile int currentDisplayBufferIdx_; 

    bool initialized_; // 内部标志，指示 VDMA 是否已初始化成功

    // --- FreeRTOS 同步机制 ---
    // 信号量：表示有多少个缓冲区现在可以被 HLS/GPU 用于渲染。
    // 初始值为 VDMA_NUM_FRAME_BUFFERS - 1 (因为一个缓冲区总是在显示)。
    SemaphoreHandle_t xRenderingBufferAvailableSemaphore_;
    // 互斥量：保护 VDMA 提交操作，防止多个任务同时提交帧。
    SemaphoreHandle_t xDisplayBufferCommitMutex_;

} VdmaDisplay;

// 构造/初始化函数
void VdmaDisplay_init_struct(VdmaDisplay* self);

/**
 * @brief 初始化 VDMA 驱动和硬件。
 *        VDMA 将配置为从 DDR 内存读取数据并输出到视频流。
 *        输出分辨率锁定为 VDMA_OUTPUT_WIDTH x VDMA_OUTPUT_HEIGHT (640x480)。
 *        此函数还会初始化所有 HLS 和 VDMA 帧缓冲区为黑色。
 * @param self 指向 VdmaDisplay 实例的指针。
 * @return XST_SUCCESS 表示成功，其他值表示失败。
 */
int VdmaDisplay_init(VdmaDisplay* self);

/**
 * @brief 获取下一个可供 HLS/GPU 写入的帧缓冲区的索引。
 *        此函数负责内部的缓冲区索引管理，确保不会写入当前正在显示的缓冲区。
 *        注意：调用此函数前，vGpuRenderTask 应该已经获取了一个 xRenderingBufferAvailableSemaphore_。
 * @param self 指向 VdmaDisplay 实例的指针。
 * @return 下一个可写入 HLS 帧缓冲区的索引 (0, 1, 或 2)。如果发生错误或未初始化，返回 -1。
 */
int VdmaDisplay_getNextHlsWriteBufferIdx(VdmaDisplay* self);

/**
 * @brief 获取特定索引的 HLS 帧缓冲区的地址。
 * @param self 指向 VdmaDisplay 实例的指针。
 * @param buffer_idx HLS 帧缓冲区的索引 (0, 1, 或 2)。
 * @return 指向该 HLS 帧缓冲区的 UINTPTR 地址。如果索引无效，返回 0。
 */
UINTPTR VdmaDisplay_getHlsFrameBufferAddr(const VdmaDisplay* self, int buffer_idx);

/**
 * @brief 获取特定索引的 VDMA 帧缓冲区的地址。
 * @param self 指向 VdmaDisplay 实例的指针。
 * @param buffer_idx VDMA 帧缓冲区的索引 (0, 1, 或 2)。
 * @return 指向该 VDMA 帧缓冲区的 UINTPTR 地址。如果索引无效，返回 0。
 */
UINTPTR VdmaDisplay_getVdmaFrameBufferAddr(const VdmaDisplay* self, int buffer_idx);

/**
 * @brief 执行从 HLS 渲染缓冲区到 VDMA 显示缓冲区的缩放、格式转换。
 *        HLS 缓冲区格式：320x240, 4Bpp BGRX。
 *        VDMA 缓冲区格式：640x480, 3Bpp BGR。
 *        此函数负责像素处理、刷新缓存，但不提交帧到 VDMA 显示，也不处理同步信号量。
 * @param self 指向 VdmaDisplay 实例的指针。
 * @param hls_buffer_idx HLS IP 写入完成的帧缓冲区的索引。
 * @return XST_SUCCESS 表示成功，其他值表示失败。
 */
int VdmaDisplay_processAndConvertFrame(VdmaDisplay* self, int hls_buffer_idx);

/**
 * @brief 将处理完成的 VDMA 就绪帧缓冲区提交给 VDMA 显示，并更新缓冲区状态。
 *        此函数负责获取互斥量，提交帧到 VDMA，并释放 xRenderingBufferAvailableSemaphore_ 信号量。
 * @param self 指向 VdmaDisplay 实例的指针。
 * @param buffer_idx 要提交给 VDMA 显示的 VDMA 就绪帧缓冲区的索引。
 * @return XST_SUCCESS 表示成功，其他值表示失败。
 */
int VdmaDisplay_presentFrameToVDMA(VdmaDisplay* self, int buffer_idx);


/**
 * @brief 获取 VDMA 配置的输出宽度。
 * @param self 指向 VdmaDisplay 实例的指针。
 * @return VDMA 输出宽度 (像素)。
 */
u32 VdmaDisplay_getOutputWidth(const VdmaDisplay* self);

/**
 * @brief 获取 VDMA 配置的输出高度。
 * @param self 指向 VdmaDisplay 实例的指针。
 * @return VDMA 输出高度 (像素)。
 */
u32 VdmaDisplay_getOutputHeight(const VdmaDisplay* self);

/**
 * @brief 获取 HLS IP 渲染的宽度。
 * @param self 指向 VdmaDisplay 实例的指针。
 * @return HLS IP 渲染宽度 (像素)。
 */
u32 VdmaDisplay_getHlsRenderWidth(const VdmaDisplay* self);

/**
 * @brief 获取 HLS IP 渲染的高度。
 * @param self 指向 VdmaDisplay 实例的指针。
 * @return HLS IP 渲染高度 (像素)。
 */
u32 VdmaDisplay_getHlsRenderHeight(const VdmaDisplay* self);

/**
 * @brief 获取 VDMA 配置的每个像素的字节数 (VDMA_BYTES_PER_PIXEL)。
 * @param self 指向 VdmaDisplay 实例的指针。
 * @return 每个像素的字节数。
 */
u32 VdmaDisplay_getBytesPerPixel(const VdmaDisplay* self);

/**
 * @brief 获取 HLS IP 配置的每个像素的字节数 (HLS_BYTES_PER_PIXEL)。
 * @param self 指向 VdmaDisplay 实例的指针。
 * @return HLS IP 每个像素的字节数。
 */
u32 VdmaDisplay_getHlsBytesPerPixel(const VdmaDisplay* self);

/**
 * @brief 获取 VDMA 单帧缓冲区的大小（字节）。
 * @param self 指向 VdmaDisplay 实例的指针。
 * @return 单帧缓冲区大小。
 */
u32 VdmaDisplay_getVdmaSingleFrameSize(const VdmaDisplay* self);

/**
 * @brief 获取 HLS 单帧缓冲区的大小（字节）。
 * @param self 指向 VdmaDisplay 实例的指针。
 * @return HLS 单帧缓冲区大小。
 */
u32 VdmaDisplay_getHlsSingleFrameSize(const VdmaDisplay* self);

/**
 * @brief 获取当前 VDMA 正在显示的帧缓冲区的索引。
 * @param self 指向 VdmaDisplay 实例的指针。
 * @return 当前 VDMA 正在显示的帧缓冲区的索引。
 */
int VdmaDisplay_getCurrentDisplayBufferIdx(const VdmaDisplay* self);

/**
 * @brief 获取字体宽度。
 * @param self 指向 VdmaDisplay 实例的指针。
 * @return 字体宽度。
 */
u32 VdmaDisplay_getFontWidth(const VdmaDisplay* self);

/**
 * @brief 获取字体高度。
 * @param self 指向 VdmaDisplay 实例的指针。
 * @return 字体高度。
 */
u32 VdmaDisplay_getFontHeight(const VdmaDisplay* self);

/**
 * @brief 在指定的 VDMA 帧缓冲区上绘制单个字符。
 *        此函数在 VdmaDisplay 模块内部封装了对 font.h 的调用。
 * @param self 指向 VdmaDisplay 实例的指针。
 * @param buffer_idx 要绘制到的 VDMA 帧缓冲区索引。
 * @param x, y 绘制起始坐标。
 * @param character 要绘制的字符。
 * @param r, g, b 字符颜色 (0-255)。
 */
void VdmaDisplay_DrawChar(const VdmaDisplay* self, int buffer_idx, int x, int y, char character, u8 r, u8 g, u8 b);

/**
 * @brief 在指定的 VDMA 帧缓冲区上绘制字符串。
 *        此函数在 VdmaDisplay 模块内部封装了对 font.h 的调用。
 * @param self 指向 VdmaDisplay 实例的指针。
 * @param buffer_idx 要绘制到的 VDMA 帧缓冲区索引。
 * @param x, y 绘制起始坐标。
 * @param text 要绘制的字符串。
 * @param r, g, b 文本颜色 (0-255)。
 */
void VdmaDisplay_DrawString(const VdmaDisplay* self, int buffer_idx, int x, int y, const char* text, u8 r, u8 g, u8 b);

/**
 * @brief 在指定的 VDMA 帧缓冲区上绘制整数。
 *        此函数在 VdmaDisplay 模块内部封装了对 font.h 的调用。
 * @param self 指向 VdmaDisplay 实例的指针。
 * @param buffer_idx 要绘制到的 VDMA 帧缓冲区索引。
 * @param x, y 绘制起始坐标。
 * @param number 要绘制的整数。
 * @param r, g, b 数字颜色 (0-255)。
 */
void VdmaDisplay_DrawNumber(const VdmaDisplay* self, int buffer_idx, int x, int y, int number, u8 r, u8 g, u8 b);


#endif // VDMA_DISPLAY_H
